﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Puzzle
{
   
    public partial class Board
    {
        public long fitness;
        public long fitnessEvaluation()
        {
            this.fitness = 0;
            
            this.fitness += simpleFitness_beta();
            return this.fitness;
        }
        private long simpleFitness_alpha()
        {
            long fitness = 0;
            long corner_points = 40;
            long edge_points = 10;
            long neighbor_points = 4;
            for (int i = 0; i < this.tiles.Count; i++)
            {

                switch (whichEdge(i))
                {
                    case edgePlacement.up_left: //top left corner tile
                        if (this.tiles[i].top == 0 && this.tiles[i].left == 0)
                        {
                            fitness += corner_points;
                        
                            if (this.tiles[i].right == this.tiles[i + 1].left)
                                fitness += neighbor_points;
                            if (this.tiles[i].bottom == this.tiles[i + 10].top)
                                fitness += neighbor_points;
                        }
                        break;
                    case edgePlacement.up: //top row of edge-touching tiles 
                        if (this.tiles[i].top == 0)
                            fitness += edge_points;
                        if (this.tiles[i].right == this.tiles[i + 1].left)
                            fitness += neighbor_points;
                        if (this.tiles[i].bottom == this.tiles[i + 10].top)
                            fitness += neighbor_points;
                        if (this.tiles[i].left == this.tiles[i - 1].right)
                            fitness += neighbor_points;
                        break;
                    case edgePlacement.up_right: //top right corner tile
                        if (this.tiles[i].top == 0 && this.tiles[i].right == 0)
                        {
                            fitness += corner_points;
                            if (this.tiles[i].left == this.tiles[i - 1].right)
                                fitness += neighbor_points;
                            if (this.tiles[i].bottom == this.tiles[i + 10].top)
                                fitness += neighbor_points;
                        }
                        break;
                    case edgePlacement.right: //right column of edge-touching tiles
                        if (this.tiles[i].right == 0)
                            fitness += edge_points;
                        if (this.tiles[i].top == this.tiles[i - 10].bottom)
                            fitness += neighbor_points;
                        if (this.tiles[i].bottom == this.tiles[i + 10].top)
                            fitness += neighbor_points;
                        if (this.tiles[i].left == this.tiles[i - 1].right)
                            fitness += neighbor_points;
                        break;
                    case edgePlacement.down_right:
                        if (this.tiles[i].bottom == 0 && this.tiles[i].right == 0)
                        {
                            fitness += corner_points;
                            if (this.tiles[i].left == this.tiles[i - 1].right)
                                fitness += neighbor_points;
                            if (this.tiles[i].top == this.tiles[i - 10].bottom)
                                fitness += neighbor_points;
                        }
                        break;
                    case edgePlacement.down://bottom row of edge-touching tiles
                        if (this.tiles[i].bottom == 0)
                            fitness += edge_points;
                        if (this.tiles[i].right == this.tiles[i + 1].left)
                            fitness += neighbor_points;
                        if (this.tiles[i].top == this.tiles[i - 10].bottom)
                            fitness += neighbor_points;
                        if (this.tiles[i].left == this.tiles[i - 1].right)
                            fitness += neighbor_points;
                        break;
                    case edgePlacement.down_left:
                        if (this.tiles[i].bottom == 0 && this.tiles[i].left == 0)
                        {
                            fitness += corner_points;
                            if (this.tiles[i].right == this.tiles[i + 1].left)
                                fitness += neighbor_points;
                            if (this.tiles[i].top == this.tiles[i - 10].bottom)
                                fitness += neighbor_points;
                        }
                        break;
                    case edgePlacement.left:
                        if (this.tiles[i].left == 0)
                            fitness += edge_points;
                        if (this.tiles[i].top == this.tiles[i - 10].bottom)
                            fitness += neighbor_points;
                        if (this.tiles[i].bottom == this.tiles[i + 10].top)
                            fitness += neighbor_points;
                        if (this.tiles[i].left == this.tiles[i - 1].right)
                            fitness += neighbor_points;
                        break;
                    case edgePlacement.none:
                        if (this.tiles[i].left == this.tiles[i - 1].right)
                            fitness += neighbor_points;
                        if (this.tiles[i].right == this.tiles[i + 1].left)
                            fitness += neighbor_points;
                        if (this.tiles[i].bottom == this.tiles[i + 10].top)
                            fitness += neighbor_points;
                        if (this.tiles[i].top == this.tiles[i - 10].bottom)
                            fitness += neighbor_points;
                        break;

                    default:
                        throw new CustomExc("default case in evalFitnessSimple should not have been reached");
                        
                }

            }
            return fitness;
        }

        public long approxTilesArranged()
        {
            return simpleFitness_beta();
        }
        public enum edgePlacement { left, up, right, down, up_left, up_right, down_left, down_right, none };
        public edgePlacement whichEdge(int i)
        {
            if (i == 0)
                return edgePlacement.up_left;
            else if (i > 0 && i < 9)
                return edgePlacement.up;
            else if (i == 9)
                return edgePlacement.up_right;
            else if (i == 19 || i == 29 || i == 39 || i == 49 || i == 59 || i == 69 || i == 79 || i == 89)
                return edgePlacement.right;
            else if (i == 99)
                return edgePlacement.down_right;
            else if (i > 90 && i < 99)
                return edgePlacement.down;
            else if (i == 90)
                return edgePlacement.down_left;
            else if (i == 10 || i == 20 || i == 30 || i == 40 || i == 50 || i == 60 || i == 70 || i == 80)
                return edgePlacement.left;

            return edgePlacement.none;
        }
        public bool onEdge(int index)
        {
            switch(whichEdge(index))
            {
                case edgePlacement.down:
                    return true;
                    break;
                case edgePlacement.up:
                    return true;
                    break;
                case edgePlacement.left:
                    return true;
                    break;
                case edgePlacement.right:
                    return true;
                    break;
                default:
                    return false;
            }

        }
        public bool onCorner(int index)
        {
            switch (whichEdge(index))
            {
                case edgePlacement.down_left:
                    return true;
                    break;
                case edgePlacement.down_right:
                    return true;
                    break;
                case edgePlacement.up_left:
                    return true;
                    break;
                case edgePlacement.up_right:
                    return true;
                    break;
                default:
                    return false;
            }
        }

        private long simpleFitness_beta()
        {
            long fitness = 0;
            long corner_points = 20;
            long edge_points = 10;
            long neighbor_points = 4;
            for (int i = 0; i < this.tiles.Count; i++)
            {

                switch (whichEdge(i))
                {
                    case edgePlacement.up_left: //top left corner tile
                        if (this.tiles[i].top == 0 && this.tiles[i].left == 0)
                        {
                            fitness += corner_points;

                            if (this.tiles[i].right == this.tiles[i + 1].left)
                                fitness += neighbor_points;
                            if (this.tiles[i].bottom == this.tiles[i + 10].top)
                                fitness += neighbor_points;
                        }
                        break;
                    case edgePlacement.up: //top row of edge-touching tiles 
                        if (this.tiles[i].top == 0)
                            fitness += edge_points;
                        if (this.tiles[i].right == this.tiles[i + 1].left)
                            fitness += neighbor_points;
                        if (this.tiles[i].bottom == this.tiles[i + 10].top)
                            fitness += neighbor_points;
                        if (this.tiles[i].left == this.tiles[i - 1].right)
                            fitness += neighbor_points;
                        break;
                    case edgePlacement.up_right: //top right corner tile
                        if (this.tiles[i].top == 0 && this.tiles[i].right == 0)
                        {
                            fitness += corner_points;
                            if (this.tiles[i].left == this.tiles[i - 1].right)
                                fitness += neighbor_points;
                            if (this.tiles[i].bottom == this.tiles[i + 10].top)
                                fitness += neighbor_points;
                        }
                        break;
                    case edgePlacement.right: //right column of edge-touching tiles
                        if (this.tiles[i].right == 0)
                            fitness += edge_points;
                        if (this.tiles[i].top == this.tiles[i - 10].bottom)
                            fitness += neighbor_points;
                        if (this.tiles[i].bottom == this.tiles[i + 10].top)
                            fitness += neighbor_points;
                        if (this.tiles[i].left == this.tiles[i - 1].right)
                            fitness += neighbor_points;
                        break;
                    case edgePlacement.down_right:
                        if (this.tiles[i].bottom == 0 && this.tiles[i].right == 0)
                        {
                            fitness += corner_points;
                            if (this.tiles[i].left == this.tiles[i - 1].right)
                                fitness += neighbor_points;
                            if (this.tiles[i].top == this.tiles[i - 10].bottom)
                                fitness += neighbor_points;
                        }
                        break;
                    case edgePlacement.down://bottom row of edge-touching tiles
                        if (this.tiles[i].bottom == 0)
                            fitness += edge_points;
                        if (this.tiles[i].right == this.tiles[i + 1].left)
                            fitness += neighbor_points;
                        if (this.tiles[i].top == this.tiles[i - 10].bottom)
                            fitness += neighbor_points;
                        if (this.tiles[i].left == this.tiles[i - 1].right)
                            fitness += neighbor_points;
                        break;
                    case edgePlacement.down_left:
                        if (this.tiles[i].bottom == 0 && this.tiles[i].left == 0)
                        {
                            fitness += corner_points;
                            if (this.tiles[i].right == this.tiles[i + 1].left)
                                fitness += neighbor_points;
                            if (this.tiles[i].top == this.tiles[i - 10].bottom)
                                fitness += neighbor_points;
                        }
                        break;
                    case edgePlacement.left:
                        if (this.tiles[i].left == 0)
                            fitness += edge_points;
                        if (this.tiles[i].top == this.tiles[i - 10].bottom)
                            fitness += neighbor_points;
                        if (this.tiles[i].bottom == this.tiles[i + 10].top)
                            fitness += neighbor_points;
                        if (this.tiles[i].left == this.tiles[i - 1].right)
                            fitness += neighbor_points;
                        break;
                    case edgePlacement.none:
                        if (this.tiles[i].left == this.tiles[i - 1].right)
                            fitness += neighbor_points;
                        if (this.tiles[i].right == this.tiles[i + 1].left)
                            fitness += neighbor_points;
                        if (this.tiles[i].bottom == this.tiles[i + 10].top)
                            fitness += neighbor_points;
                        if (this.tiles[i].top == this.tiles[i - 10].bottom)
                            fitness += neighbor_points;
                        break;

                    default:
                        throw new CustomExc("default case in evalFitnessSimple should not have been reached");

                }

            }
            return fitness;
        }
    }
}
